﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace kripto2._1.operation
{
    class innerFunction
    {
        /// <summary>
        /// prosedur untuk mendapatkan inner statekey dari inner key (jika merupakan inner statekey pertama)
        /// atau dari inner statekey sebelumnya (jika bukan merupakan inner statekey pertama) 
        /// menggunakan operasi subtitusi
        /// </summary>
        /// <param name="innerKey">inner key atau inner statekey sebelumnya</param>
        /// <param name="keyLength">ukuran array innerKey</param>
        /// <returns>inner state key</returns>
        private static byte[] getInnerStateKey(byte[] innerKey)
        {
            byte[] innerStateKey = new byte[innerKey.Length];
            byte[,] sBox = { {92, 234, 38, 18, 244, 114, 248, 85, 193, 39, 57, 126, 238, 61, 163, 191, 151, 239, 112, 87, 252, 72, 66, 98, 137, 94},
                             {90, 165, 113, 53, 236, 69, 240, 255, 227, 177, 124, 221, 79, 106, 115, 23, 86, 110, 158, 9, 128, 89, 188, 147, 122, 19},
                             {168, 196, 136, 150, 24, 82, 139, 59, 243, 141, 241, 67, 185, 160, 6, 228, 216, 11, 27, 81, 37, 223, 48, 60, 132, 207},
                             {184, 84, 32, 52, 25, 46, 99, 224, 176, 174, 142, 22, 156, 2, 149, 208, 109, 214, 198, 45, 203, 172, 182, 49, 105, 232},
                             {101, 231, 180, 138, 169, 58, 15, 162, 1, 20, 173, 226, 242, 104, 108, 35, 175, 70, 55, 77, 230, 50, 36, 202, 247, 123},
                             {131, 44, 5, 41, 209, 71, 29, 133, 251, 201, 117, 237, 195, 10, 119, 102, 167, 253, 166, 164, 93, 111, 155, 233, 116, 26},
                             {135, 249, 118, 83, 170, 220, 12, 30, 192, 74, 21, 91, 100, 95, 73, 222, 42, 189, 3, 68, 161, 17, 0, 146, 13, 107},
                             {154, 88, 148, 34, 217, 204, 14, 97, 218, 153, 8, 31, 40, 199, 159, 210, 54, 56, 206, 250, 219, 7, 76, 62, 215, 140},
                             {194, 245, 186, 171, 64, 144, 254, 121, 63, 65, 213, 33, 190, 143, 43, 229, 235, 200, 212, 225, 179, 130, 47, 181, 4, 187},
                             {127, 125, 129, 103, 78, 205, 246, 197, 75, 134, 80, 28, 178, 96, 152, 145, 120, 16, 183, 211, 51, 157, 0, 0, 0, 0} };
            int satuan, puluhan;
            for (int i = 0; i < innerKey.Length; i++)
            {
                puluhan = innerKey[i] / 10;
                satuan = innerKey[i] - (puluhan * 10);
                innerStateKey[i] = sBox[satuan, puluhan];
            }
            return innerStateKey;
        }

        /// <summary>
        /// prosedur untuk membuat SBox yang digenerate dari inner statekey
        /// </summary>
        /// <param name="key">inner statekey</param>
        /// <returns>SBox dalam bentuk matriks berukuran 16 x 16</returns>
        private static byte[,] generateSBox(byte[] key)
        {
            byte[,] sBox = new byte[16, 16];

            int seed = 0;
            for (int i = 0; i < key.Length; i++)
            {
                seed += key[i];
            }

            bool[] isi = new bool[256];
            for (int i = 0; i < 256; i++)
            {
                isi[i] = false;
            }

            Random rand = new Random(seed);
            int temp;
            for (int i = 0; i < 16; i++)
            {
                for (int j = 0; j < 16; j++)
                {
                    temp = rand.Next() % 256;
                    while (isi[temp])
                    {
                        temp = rand.Next() % 256;
                    }
                    sBox[i, j] = (byte)temp;
                    isi[temp] = true;
                }
            }

            return sBox;
        }

        /// <summary>
        /// prosedur untuk membaca SBox 
        /// </summary>
        /// <param name="SBox">SBox dalam bentuk matriks 16 x 16</param>
        /// <param name="hex">Heksadesimal (dalam bentuk string) yang akan disubtitusi</param>
        /// <returns>Reverse SBox dalam bentuk array of byte berukuran 256</returns>
        private static byte readSBox(byte[,] SBox, String hex)
        {
            int MSB = 0, LSB = 0;
            if (hex.Length == 2)
            {
                MSB = Convert.ToInt32(hex.Substring(0, 1), 16);
                LSB = Convert.ToInt32(hex.Substring(1, 1), 16);
            }
            else
            {
                MSB = 0;
                LSB = Convert.ToInt32(hex.Substring(0, 1), 16);
            }
            return SBox[MSB, LSB];
        }

        /// <summary>
        /// prosedur untuk membaca SBox secara terbalik
        /// </summary>
        /// <param name="reverseSBox">SBox dalam bentuk matriks 16 x 16</param>
        /// <param name="value">byte yang akan dikembalikan</param>
        /// <returns>hasil berupa heksadesimal(dalam bentuk string)</returns>
        private static String reverseSBox(byte[,] SBox, byte value)
        {
            int MSB = 0, LSB = 0;
            while (SBox[MSB, LSB] != value)
            {
                LSB++;
                if (LSB == 16)
                {
                    MSB++;
                    LSB = 0;
                }
            }
            return MSB.ToString("X") + LSB.ToString("X");
        }

        /// <summary>
        /// prosedur enkripsi dengan transposisi kemudian dengan cipher berulang sebanyak 3 round
        /// </summary>
        /// <param name="plaintext">4 byte plain teks</param>
        /// <param name="innerKey">4 byte key</param>
        /// <returns>4 byte cipher teks</returns>
        public static byte[] innerEnkrip(byte[] plaintext, byte[] innerKey)
        {
            int setengahBlok = innerKey.Length;
            byte[] ciphertext = new byte[setengahBlok];
            byte[] statePlainText = new byte[setengahBlok];
            byte[] stateCipherTextSub = new byte[setengahBlok];
            byte[] stateCipherTextXOR = new byte[setengahBlok];
            byte[] stateCipherTextTpose = new byte[setengahBlok];
            byte[][] innerStateKey = new byte[3][];
            innerStateKey[0] = getInnerStateKey(innerKey);
            innerStateKey[1] = getInnerStateKey(innerStateKey[0]);
            innerStateKey[2] = getInnerStateKey(innerStateKey[1]);

            plaintext.CopyTo(statePlainText, 0);
            for (int i = 0; i < 3; i++)
            {
                //Pengacakan dengan Subtitusi Hexadecimal
                byte[,] sBox = generateSBox(innerStateKey[i]);
                String[] hex = new String[setengahBlok];
                for (int j = 0; j < setengahBlok; j++)
                {
                    hex[j] = statePlainText[j].ToString("X");
                    stateCipherTextSub[j] = readSBox(sBox, hex[j]);
                }

                //XOR Plaintext dengan kunci
                //plaintext byte terakhir di XOR dengan key byte pertama
                stateCipherTextXOR[0] = (byte)((int)stateCipherTextSub[setengahBlok - 1] ^ (int)innerStateKey[i][0]);

                //plaintext ke-1,2,..,n-1 di XOR dengan key byte ke-2,3,..,n
                for (int j = 0; j < setengahBlok - 1; j++)
                {
                    stateCipherTextXOR[j + 1] = (byte)((int)stateCipherTextSub[j] ^ (int)innerStateKey[i][j + 1]);
                }

                //Pengacakan dengan Transposisi bit
                int regangan = 3;
                while ((8 * setengahBlok) % regangan == 0)
                {
                    regangan++;
                }

                int totalkey = 0;
                for (int j = 0; j < setengahBlok; j++)
                {
                    totalkey += (int)innerStateKey[i][j];
                }

                BitArray bits = new BitArray(stateCipherTextXOR);
                BitArray bitsAcak = new BitArray(bits.Length);
                for (int j = 0; j < 8 * setengahBlok; j++)
                {
                    bitsAcak[j] = bits[(totalkey + (regangan * j)) % (8 * setengahBlok)];
                }
                bitsAcak.CopyTo(stateCipherTextTpose, 0);

                statePlainText = stateCipherTextTpose;
            }
            ciphertext = stateCipherTextTpose;
            return ciphertext;
        }

        /// <summary>
        /// prosedur dekripsi dengan transposisi kemudian dengan cipher berulang sebanyak 3 round
        /// </summary>
        /// <param name="ciphertext">4 byte cipher teks</param>
        /// <param name="innerKey">4 byte key</param>
        /// <returns>4 byte plain teks</returns>
        public static byte[] innerDekrip(byte[] ciphertext, byte[] innerKey)
        {
            int setengahBlok = innerKey.Length;
            byte[] plaintext = new byte[setengahBlok];
            byte[] stateCipherText = new byte[setengahBlok];
            byte[] statePlainTextTpose = new byte[setengahBlok];
            byte[] statePlainTextXOR = new byte[setengahBlok];
            byte[] statePlainTextSub = new byte[setengahBlok];
            byte[][] innerStateKey = new byte[3][];
            innerStateKey[0] = getInnerStateKey(innerKey);
            innerStateKey[1] = getInnerStateKey(innerStateKey[0]);
            innerStateKey[2] = getInnerStateKey(innerStateKey[1]);

            ciphertext.CopyTo(stateCipherText, 0);
            for (int i = 0; i < 3; i++)
            {
                //Reverse Pengacakan dengan Transposisi bit
                int regangan = 3;
                while ((8 * setengahBlok) % regangan == 0)
                {
                    regangan++;
                }

                int totalkey = 0;
                for (int j = 0; j < setengahBlok; j++)
                {
                    totalkey += (int)innerStateKey[2 - i][j];
                }

                BitArray bitsAcak = new BitArray(stateCipherText);
                BitArray bits = new BitArray(bitsAcak.Length);
                for (int j = 0; j < 8 * setengahBlok; j++)
                {
                    bits[(totalkey + (regangan * j)) % (8 * setengahBlok)] = bitsAcak[j];
                }
                bits.CopyTo(statePlainTextTpose, 0);

                //XOR Ciphertext dengan kunci
                //ciphertext byte pertama di XOR dengan key byte pertama
                statePlainTextXOR[setengahBlok - 1] = (byte)((int)statePlainTextTpose[0] ^ (int)innerStateKey[2 - i][0]);

                //ciphertect ke-2,3,..,n di XOR dengan key byte ke-2,3,..,n
                for (int j = 0; j < setengahBlok - 1; j++)
                {
                    statePlainTextXOR[j] = (byte)((int)statePlainTextTpose[j + 1] ^ (int)innerStateKey[2 - i][j + 1]);
                }

                //Reverse pengacakan dengan Subtitusi Hexadecimal
                //Pengacakan dengan Subtitusi Hexadecimal
                byte[,] SBox = generateSBox(innerStateKey[2 - i]);
                for (int j = 0; j < setengahBlok; j++)
                {
                    String hex = reverseSBox(SBox, statePlainTextXOR[j]);
                    statePlainTextSub[j] = (byte)(Convert.ToInt32(hex, 16));
                }
                stateCipherText = statePlainTextSub;

            }

            plaintext = statePlainTextSub;
            return plaintext;
        }

    }
}
